Skip to content

Swap Atlassian MCP for the working jira MCP (sooperset/mcp-atlassian) (#528)#531

Merged
dhilgaertner merged 1 commit into
mainfrom
feature/crow-528-swap-jira-mcp
Jun 19, 2026
Merged

Swap Atlassian MCP for the working jira MCP (sooperset/mcp-atlassian) (#528)#531
dhilgaertner merged 1 commit into
mainfrom
feature/crow-528-swap-jira-mcp

Conversation

@dhilgaertner

Copy link
Copy Markdown
Contributor

Closes #528. Partially reverts #522/#524.

What

The Atlassian Remote MCP Server (mcp.atlassian.com, server atlassian, HTTP Basic via ATLASSIAN_MCP_AUTHORIZATION) added in #522/#524 never worked in our setup. This routes all agent-side Jira tooling through the jira MCP (sooperset/mcp-atlassian, Docker stdio) that is already configured globally in ~/.claude.json and confirmed working.

Decisions (called out per the ticket)

  • Injection vs global → global. The jira server lives in ~/.claude.json's top-level mcpServers, so it's auto-loaded and trusted in every Claude Code session (worktrees, Manager, cron). Crow injects nothing — no per-session .mcp.json, no enabledMcpjsonServers.
  • Secret handling. No Crow-side secret for the agent flow (the global server owns JIRA_URL/JIRA_USERNAME/JIRA_API_TOKEN). The one exception: the in-app "Fetch from Jira" status-map button (Settings UI to map Crow ticket states ↔ Jira workflow statuses #523) runs in the Swift app process, which cannot use the MCP — it keeps a small Jira (status fetch) credential (username + op:// token) under Settings → Automation, used only to call Jira REST directly.

Changes

  • setup.sh + bundled template: removed resolve_atlassian_mcp_env, the settings.local.json ATLASSIAN_MCP_AUTHORIZATION + enabledMcpjsonServers injection, write_mcp_json, and the .mcp.json git-exclude.
  • App: deleted ClaudeHookConfigWriter.writeAtlassianMcpConfig, the SessionService injection sites + resolver helpers, and the injection test. Renamed AtlassianMCPConfigJiraCredential and AtlassianMCPResolverJiraCredentialResolver; AppConfig.atlassianMCPjiraCredential with backward-compat decode of the old block. Settings section Atlassian MCPJira (status fetch). Scaffolder allowlist mcp__atlassian*mcp__jira*.
  • Skills + docs: rewrote the Jira sections to the verified jira_* tools (jira_get_issue / jira_create_issue / jira_update_issue / jira_transition_issue + jira_get_transitions / jira_add_comment / jira_get_user_profile), dropped the cloudId / getAccessibleAtlassianResources step, and documented the two-step transition flow (jira_get_transitionstransition_idjira_transition_issue).
  • Removed the stale atlassian entry from the live devRoot .mcp.json + settings.local.json.

Verification

  • make app builds; make test green for all touched packages (CrowCore, CrowClaude, CrowUI, root — incl. the new JiraCredentialResolverTests and the skill/template byte-identical sync tests). The only failing suite is CrowGit RebaseTests, a pre-existing sandbox env issue (no git config user.email/user.name in the temp test repos) unrelated to this change.
  • rg sweep confirms no remaining mcp.atlassian.com / ATLASSIAN_MCP_AUTHORIZATION / atlassian MCP server / old tool names (getJiraIssue, etc.) in setup.sh, skills, app, or .mcp.json — except the intentional backward-compat migration decoder + its test fixture.

Coordination

Land before #529 (status-transition audit) and alongside #523 (jiraStatusMap) — the transition tool is now jira_transition_issue.

🤖 Generated with Claude Code

…ian) (#528)

The Atlassian Remote MCP Server added in #522/#524 (mcp.atlassian.com,
server name `atlassian`, HTTP Basic via ATLASSIAN_MCP_AUTHORIZATION) never
worked in our setup. Route everything Jira through the `jira` MCP
(sooperset/mcp-atlassian, Docker stdio) that is already configured globally
in ~/.claude.json and confirmed working.

Decision: rely on the global `jira` server. Because it lives in
~/.claude.json's top-level mcpServers, it is auto-loaded and trusted in every
Claude Code session (worktrees, Manager, cron) — so Crow injects nothing.

- setup.sh / bundled template: drop resolve_atlassian_mcp_env, the
  settings.local.json ATLASSIAN_MCP_AUTHORIZATION + enabledMcpjsonServers
  injection, write_mcp_json, and the .mcp.json git-exclude.
- App: delete ClaudeHookConfigWriter.writeAtlassianMcpConfig, the
  SessionService injection call sites + resolver helpers, and the MCP
  injection test. The Swift app process can't use the MCP, so the #523
  "Fetch from Jira" status button keeps a small REST credential: rename
  AtlassianMCPConfig -> JiraCredential (username + op:// token, no endpoint)
  and AtlassianMCPResolver -> JiraCredentialResolver (Basic header). AppConfig
  field atlassianMCP -> jiraCredential, with backward-compat decode of the old
  block. Settings "Atlassian MCP" section -> "Jira (status fetch)".
- Skills + docs: rewrite the Jira sections to the real jira_* tools
  (jira_get_issue/create_issue/update_issue/transition_issue/get_transitions/
  add_comment/get_user_profile), drop the cloudId / getAccessibleAtlassianResources
  step, and document the two-step transition flow (get_transitions -> transition_id).
- Scaffolder allowlist mcp__atlassian* -> mcp__jira*. Removed the stale
  atlassian entry from the devRoot .mcp.json + settings.local.json.

🐦‍⬛ Generated with Claude Code, orchestrated by Crow

Co-Authored-By: Claude <noreply@anthropic.com>
Crow-Session: AB7A2CCD-FF91-44FB-99B1-6A06F1380859
@dhilgaertner dhilgaertner requested a review from dgershman as a code owner June 19, 2026 19:40
@dhilgaertner dhilgaertner added the crow:merge Crow auto-merge on green label Jun 19, 2026

@dgershman dgershman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code & Security Review

Critical Issues

None.

Security Review

Strengths:

  • Secret handling unchanged in shape — tokenRef still accepts op://… references and resolves on demand; plaintext fallback stays in config.json (owner-only 0600), matching the gateway secret rules.
  • JiraCredentialResolver carefully avoids logging the resolved token or header (only "no username/no token/op read failed" diagnostics).
  • Net removes a wire-format attack surface: the previous design wrote the resolved Basic … header into settings.local.json's env block and a .mcp.json referencing it; both are gone. The Crow process now only constructs the header in-memory for direct REST calls.
  • Packages/CrowCore/Sources/CrowCore/JiraStatusFetcher.swift:7 — direct REST call still uses Jira's GET /rest/api/3/project/{key}/statuses with HTTP Basic over TLS, which is appropriate.

Concerns:

  • None blocking. The migration of atlassianMCPjiraCredential preserves the same secret-storage rules, and the legacy endpoint field is intentionally dropped (no longer needed since this credential is only for the in-app REST fetch).

Code Quality

  • Packages/CrowCore/Sources/CrowCore/Models/AppConfig.swift:145-181 — the backward-compat migration uses a separate LegacyCodingKeys decode-only container so atlassianMCP stays out of the synthesized encode(to:). This is the right shape: one-way migration; on first save the legacy key is silently dropped, and appConfigMigratesLegacyAtlassianMCP proves the round-trip.
  • Packages/CrowCore/Sources/CrowCore/JiraCredentialResolver.swift:21-54 — clean, injectable secret resolver mirroring the gateway flow. Test coverage for empty / half-configured / op-read-failure / plaintext / op:// paths is thorough.
  • Sources/Crow/App/SessionService.swift — deletion of workspaceAtlassianMCPResolved(for:) and the per-worktree MCP writer call sites is consistent with ClaudeHookConfigWriter.writeAtlassianMcpConfig being removed. No orphans.
  • Sources/Crow/App/Scaffolder.swift:488-489 — allowlist correctly swapped from mcp__atlassian* to mcp__jira* to match the global server name. Note: existing Bash(acli jira workitem …) allowlist entries are kept (still used by the in-app issue-board polling / Mark in Review path) — appropriate scope.
  • Templates and skills stay byte-identical (verified via diff); the sync tests already enforce this.
  • rg confirms no stale references to mcp.atlassian.com, ATLASSIAN_MCP_AUTHORIZATION, mcp__atlassian, or the old tool names (getJiraIssue, etc.) — except the intentional migration decoder + its test fixture.

Minor (Green — consider, non-blocking):

  • The PR depends on the user having configured the jira server globally in ~/.claude.json. setup.sh does not pre-flight for it and skills don't include a troubleshooting note. If the global server is missing, the agent flow will fail with a confusing "no such tool: jira_get_issue" error. Worth a single log line in setup.sh (or skill troubleshooting bullet) pointing users at docs/automation.md#jira-mcp when taskProvider: "jira" is set — purely UX, not a defect of this PR. Out of scope here.

Verification

  • swift build --package-path Packages/CrowCore → clean.
  • swift test --package-path Packages/CrowCore --filter JiraCredentialResolver → all 8 tests pass (resolver paths + legacy migration + round-trip).
  • Template ↔ skill diffs → byte-identical.

Summary Table

Color Meaning Verdict effect
Red Must fix Request changes
Yellow Should fix Request changes
Green Consider Approve allowed

Recommendation: Approve — driven by 0 Red, 0 Yellow, 1 Green finding.


🐦‍⬛ Reviewed by Crow via Claude Code

@dhilgaertner dhilgaertner merged commit 545ec80 into main Jun 19, 2026
2 checks passed
@dhilgaertner dhilgaertner deleted the feature/crow-528-swap-jira-mcp branch June 19, 2026 19:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

crow:merge Crow auto-merge on green

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Swap the Atlassian MCP for the working jira MCP (sooperset/mcp-atlassian) across setup.sh, skills, and app

2 participants